<!-- Demo version: 2017.08.06 -->

<!DOCTYPE html>
<html lang="en">

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://cdn.webrtc-experiment.com/style.css">

    <title>WebRTC Scalable File Sharing using RTCMultiConnection</title>

    <meta name="description" content="This module simply initializes socket.io and configures it in a way that single file can be shared/relayed over unlimited users without any bandwidth/CPU usage issues. Everything happens peer-to-peer!" />
    <meta name="keywords" content="WebRTC,RTCMultiConnection,Demos,Experiments,Samples,Examples" />

    <style>
        video {
            object-fit: fill;
            width: 30%;
        }
        button,
        input,
        select {
            font-weight: normal;
            padding: 2px 4px;
            text-decoration: none;
            display: inline-block;
            text-shadow: none;
            font-size: 16px;
            outline: none;
        }

        .make-center {
            text-align: center;
            padding: 5px 10px;
        }

        button, input, select {
            font-family: Myriad, Arial, Verdana;
            font-weight: normal;
            border-top-left-radius: 3px;
            border-top-right-radius: 3px;
            border-bottom-right-radius: 3px;
            border-bottom-left-radius: 3px;
            padding: 4px 12px;
            text-decoration: none;
            color: rgb(27, 26, 26);
            display: inline-block;
            box-shadow: rgb(255, 255, 255) 1px 1px 0px 0px inset;
            text-shadow: none;
            background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(0.05, rgb(241, 241, 241)), to(rgb(230, 230, 230)));
            font-size: 20px;
            border: 1px solid red;
            outline:none;
        }

        button:hover, input:hover, select:hover {
            background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(5%, rgb(221, 221, 221)), to(rgb(250, 250, 250)));
            border: 1px solid rgb(142, 142, 142);
        }

        button:active, input:active, select:active, button:focus, input:focus, select:focus {
            background: -webkit-gradient(linear, 0% 0%, 0% 100%, color-stop(5%, rgb(183, 183, 183)), to(rgb(255, 255, 255)));
            border: 1px solid rgb(142, 142, 142);
        }
        button[disabled], iput[disabled], select[disabled] {
            background: rgb(249, 249, 249);
            border: 1px solid rgb(218, 207, 207);
            color: rgb(197, 189, 189);
        }
        input, input:focus, input:active {
            background: white;
        }
    </style>
</head>

<body>
    <article>

        <header style="text-align: center;">
            <h1><a href="https://github.com/muaz-khan/WebRTC-Scalable-Broadcast">WebRTC Scalable File Sharing</a> using <a href="https://github.com/muaz-khan/RTCMultiConnection">RTCMultiConnection</a></h1>
            <p>
                <a href="https://rtcmulticonnection.herokuapp.com/">HOME</a>
                <span> &copy; </span>
                <a href="http://www.MuazKhan.com/" target="_blank">Muaz Khan</a> .
                <a href="http://twitter.com/WebRTCWeb" target="_blank" title="Twitter profile for WebRTC Experiments">@WebRTCWeb</a> .
                <a href="https://github.com/muaz-khan?tab=repositories" target="_blank" title="Github Profile">Github</a> .
                <a href="https://github.com/muaz-khan/RTCMultiConnection/issues?state=open" target="_blank">Latest issues</a> .
                <a href="https://github.com/muaz-khan/RTCMultiConnection/commits/master" target="_blank">What's New?</a>
            </p>
        </header>

        <div class="github-stargazers"></div>

        <section class="experiment">
            <div class="make-center">
                <input type="text" id="broadcast-id" placeholder="broadcast-id" value="room-xyz">
                <button id="open-or-join" disabled>Open or Join Broadcast</button>

                <div id="room-urls" style="text-align: center;display: none;background: #F1EDED;margin: 15px -10px;border: 1px solid rgb(189, 189, 189);border-left: 0;border-right: 0;"></div>

                <br><br>
                <input type="file">
            </div>

            <div id="files-container"></div>
        </section>

        <blockquote>
            This module simply initializes socket.io and configures it in a way that single file can be shared/relayed over unlimited users without any <a href="https://www.webrtc-experiment.com/docs/RTP-usage.html">bandwidth/CPU usage issues</a>. Everything happens peer-to-peer!

            <br><br>
            Share camera with unlimited users using p2p methods! <a href="/demos/Video-Scalable-Broadcast.html">Video Scalable Broadcast</a> or <a href="/demos/Scalable-Broadcast.html">all-in-one scalable broadcast</a>.
        </blockquote>

        <script src="/dist/RTCMultiConnection.min.js"></script>
        <script src="/node_modules/webrtc-adapter/out/adapter.js"></script>
        <script src="/socket.io/socket.io.js"></script>

        <script src="/node_modules/fbr/FileBufferReader.js"></script>

        <script>
            var connection = new RTCMultiConnection();

            // by default, socket.io server is assumed to be deployed on your own URL
            connection.socketURL = '/';

            // comment-out below line if you do not have your own socket.io server
            // connection.socketURL = 'https://rtcmulticonnection.herokuapp.com:443/';

            connection.socketMessageEvent = 'files-scalable-broadcast-demo';

            connection.enableScalableBroadcast = true;
            connection.maxRelayLimitPerUser = 1;

            connection.fileReceived = {};
            connection.enableFileSharing = true;
            connection.chunkSize = 60 * 1000;

            document.getElementById('broadcast-id').value = connection.token();
            connection.filesContainer = document.getElementById('files-container');
            connection.sdpConstraints.mandatory = {
                OfferToReceiveAudio: false,
                OfferToReceiveVideo: false
            };

            connection.onopen = function(event) {
                if (connection.isInitiator) {
                    if (connection.selectedFile) {
                        connection.send(connection.selectedFile, event.userid);
                    }
                    return;
                }

                document.querySelector('input[type=file]').disabled = true;

                if (connection.isInitiatorConnected && connection.lastFile) {
                    connection.send(connection.lastFile, event.userid);
                }

                if (!connection.isInitiatorConnected) {
                    connection.initiatorId = event.userid;
                    connection.isInitiatorConnected = true;
                }
            };

            // ask node.js server to look for a broadcast
            // if broadcast is available, simply join it. i.e. "join-broadcaster" event should be emitted.
            // if broadcast is absent, simply create it. i.e. "start-broadcasting" event should be fired.
            document.getElementById('open-or-join').onclick = function() {
                var broadcastId = document.getElementById('broadcast-id').value;
                if (broadcastId.replace(/^\s+|\s+$/g, '').length <= 0) {
                    alert('Please enter broadcast-id');
                    document.getElementById('broadcast-id').focus();
                    return;
                }

                document.getElementById('open-or-join').disabled = true;

                connection.session = {
                    data: true,
                    oneway: true
                };

                connection.socket.emit('join-broadcast', {
                    broadcastId: broadcastId,
                    userid: connection.userid,
                    typeOfStreams: connection.session
                });
            };

            connection.connectSocket(function(socket) {
                connection.socket = socket;
                document.getElementById('open-or-join').disabled = false;

                // this event is emitted when a broadcast is already created.
                connection.socket.on('join-broadcaster', function(hintsToJoinBroadcast) {
                    connection.session = hintsToJoinBroadcast.typeOfStreams;
                    connection.sdpConstraints.mandatory = {
                        OfferToReceiveVideo: !!connection.session.video,
                        OfferToReceiveAudio: !!connection.session.audio
                    };

                    connection.join(hintsToJoinBroadcast.userid);
                });

                // this event is emitted when a broadcast is absent.
                connection.socket.on('start-broadcasting', function(typeOfStreams) {
                    // host i.e. sender should always use this!
                    connection.sdpConstraints.mandatory = {
                        OfferToReceiveVideo: false,
                        OfferToReceiveAudio: false
                    };
                    connection.session = typeOfStreams;
                    connection.open(connection.userid, function() {
                        showRoomURL(connection.sessionid);
                    });
                });

                socket.on('logs', function(log) {
                    document.querySelector('h1').innerHTML = log.replace(/</g, '----').replace(/>/g, '___').replace(/----/g, '(<span style="color:red;">').replace(/___/g, '</span>)');
                });
            });

            window.onbeforeunload = function() {
                // Firefox is weird!
                document.getElementById('open-or-join').disabled = false;
            };

            var FileProgressBarHandler = (function() {
                function handle(connection) {
                    var progressHelper = {};

                    // www.RTCMultiConnection.org/docs/onFileStart/
                    connection.onFileStart = function(file) {
                        if (connection.fileReceived[file.name]) return;

                        var div = document.createElement('div');
                        div.id = file.uuid;
                        div.title = file.name;
                        div.innerHTML = '<label>0%</label> <progress></progress>';

                        if (file.remoteUserId) {
                            div.innerHTML += ' (Sharing with:' + file.remoteUserId + ')';
                        }

                        connection.filesContainer.insertBefore(div, connection.filesContainer.firstChild);

                        if (!file.remoteUserId) {
                            progressHelper[file.uuid] = {
                                div: div,
                                progress: div.querySelector('progress'),
                                label: div.querySelector('label')
                            };
                            progressHelper[file.uuid].progress.max = file.maxChunks;
                            return;
                        }

                        if (!progressHelper[file.uuid]) {
                            progressHelper[file.uuid] = {};
                        }

                        progressHelper[file.uuid][file.remoteUserId] = {
                            div: div,
                            progress: div.querySelector('progress'),
                            label: div.querySelector('label')
                        };
                        progressHelper[file.uuid][file.remoteUserId].progress.max = file.maxChunks;
                    };

                    // www.RTCMultiConnection.org/docs/onFileProgress/
                    connection.onFileProgress = function(chunk) {
                        if (connection.fileReceived[chunk.name]) return;

                        var helper = progressHelper[chunk.uuid];
                        if (!helper) {
                            return;
                        }
                        if (chunk.remoteUserId) {
                            helper = progressHelper[chunk.uuid][chunk.remoteUserId];
                            if (!helper) {
                                return;
                            }
                        }

                        helper.progress.value = chunk.currentPosition || chunk.maxChunks || helper.progress.max;
                        updateLabel(helper.progress, helper.label);
                    };

                    // www.RTCMultiConnection.org/docs/onFileEnd/
                    connection.onFileEnd = function(file) {
                        if (connection.fileReceived[file.name]) return;

                        if (file.userid == connection.userid) {
                            connection.fileReceived[file.name] = file;
                        }

                        var helper = progressHelper[file.uuid];
                        if (!helper) {
                            return;
                        }

                        if (file.remoteUserId) {
                            helper = progressHelper[file.uuid][file.remoteUserId];
                            if (!helper) {
                                return;
                            }
                        }

                        var div = helper.div;
                        if (file.type.indexOf('image') != -1) {
                            div.innerHTML = '<a href="' + file.url + '" download="' + file.name + '">Download <strong style="color:red;">' + file.name + '</strong> </a><br /><img src="' + file.url + '" title="' + file.name + '" style="max-width: 80%;">';
                        } else if (file.type.indexOf('video/') != -1) {
                            div.innerHTML = '<a href="' + file.url + '" download="' + file.name + '">Download <strong style="color:red;">' + file.name + '</strong> </a><br /><video src="' + file.url + '" title="' + file.name + '" style="max-width: 80%;" controls></video>';
                        } else if (file.type.indexOf('audio/') != -1) {
                            div.innerHTML = '<a href="' + file.url + '" download="' + file.name + '">Download <strong style="color:red;">' + file.name + '</strong> </a><br /><audio src="' + file.url + '" title="' + file.name + '" style="max-width: 80%;" controls></audio>';
                        } else {
                            div.innerHTML = '<a href="' + file.url + '" download="' + file.name + '">Download <strong style="color:red;">' + file.name + '</strong> </a><br /><iframe src="' + file.url + '" title="' + file.name + '" style="width: 80%;border: 0;height: inherit;margin-top:1em;"></iframe>';
                        }

                        if (!file.slice) {
                            return;
                        }

                        if (file.slice && file.userid !== connection.userid) {
                            connection.getAllParticipants().forEach(function(paricipantId) {
                                if (paricipantId != file.userid) {
                                    connection.send(file, paricipantId);
                                }
                            });
                            connection.lastFile = file;
                        }
                    };

                    function updateLabel(progress, label) {
                        if (progress.position === -1) {
                            return;
                        }

                        var position = +progress.position.toFixed(2).split('.')[1] || 100;
                        label.innerHTML = position + '%';
                    }
                }

                return {
                    handle: handle
                };
            })();

            FileProgressBarHandler.handle(connection);

            document.querySelector('input[type=file]').onchange = function() {
                var file = this.files[0];
                if (!file) return;

                file.uuid = connection.userid;
                connection.selectedFile = file;
                if (connection.isInitiator) {
                    if (connection.getAllParticipants().length > 0) {
                        connection.send(file);
                    }
                }
            };

            function disableInputButtons() {
                document.getElementById('open-or-join').disabled = true;
                document.getElementById('broadcast-id').disabled = true;
            }

            // ......................................................
            // ......................Handling broadcast-id................
            // ......................................................

            function showRoomURL(broadcastId) {
                var roomHashURL = '#' + broadcastId;
                var roomQueryStringURL = '?broadcastId=' + broadcastId;

                var html = '<h2>Unique URL for your room:</h2><br>';

                html += 'Hash URL: <a href="' + roomHashURL + '" target="_blank">' + roomHashURL + '</a>';
                html += '<br>';
                html += 'QueryString URL: <a href="' + roomQueryStringURL + '" target="_blank">' + roomQueryStringURL + '</a>';

                var roomURLsDiv = document.getElementById('room-urls');
                roomURLsDiv.innerHTML = html;

                roomURLsDiv.style.display = 'block';
            }

            (function() {
                var params = {},
                    r = /([^&=]+)=?([^&]*)/g;

                function d(s) {
                    return decodeURIComponent(s.replace(/\+/g, ' '));
                }
                var match, search = window.location.search;
                while (match = r.exec(search.substring(1)))
                    params[d(match[1])] = d(match[2]);
                window.params = params;
            })();

            var broadcastId = '';
            if (localStorage.getItem(connection.socketMessageEvent)) {
                broadcastId = localStorage.getItem(connection.socketMessageEvent);
            } else {
                broadcastId = connection.token();
            }
            document.getElementById('broadcast-id').value = broadcastId;
            document.getElementById('broadcast-id').onkeyup = function() {
                localStorage.setItem(connection.socketMessageEvent, this.value);
            };

            var hashString = location.hash.replace('#', '');
            if(hashString.length && hashString.indexOf('comment-') == 0) {
              hashString = '';
            }

            var broadcastId = params.broadcastId;
            if(!broadcastId && hashString.length) {
                broadcastId = hashString;
            }

            if(broadcastId && broadcastId.length) {
                document.getElementById('broadcast-id').value = broadcastId;
                localStorage.setItem(connection.socketMessageEvent, broadcastId);

                // auto-join-room
                (function reCheckRoomPresence() {
                    connection.checkPresence(broadcastId, function(isRoomExists) {
                        if(isRoomExists) {
                            document.getElementById('open-or-join').onclick();
                            return;
                        }

                        setTimeout(reCheckRoomPresence, 5000);
                    });
                })();

                disableInputButtons();
            }
        </script>

        <section class="experiment own-widgets latest-commits">
            <h2 class="header" id="updates" style="color: red;padding-bottom: .1em;"><a href="https://github.com/muaz-khan/RTCMultiConnection/commits/master">Latest Updates</a></h2>
            <div id="github-commits"></div>
        </section>

        <section class="experiment own-widgets">
            <h2 class="header" id="updates" style="color: red;padding-bottom: .1em;"><a href="https://github.com/muaz-khan/RTCMultiConnection/issues">Latest Issues</a></h2>
            <div id="github-issues"></div>
        </section>

        <section class="experiment">
            <h2 class="header" id="feedback">Feedback</h2>
            <div>
                <textarea id="message" style="height: 8em; margin: .2em; width: 98%; border: 1px solid rgb(189, 189, 189); outline: none; resize: vertical;" placeholder="Have any message? Suggestions or something went wrong?"></textarea>
            </div>
            <button id="send-message" style="font-size: 1em;">Send Message</button><small style="margin-left:1em;">Enter your email too; if you want "direct" reply!</small>
        </section>

        <a href="https://github.com/muaz-khan/RTCMultiConnection" class="fork-left"></a>

        <script>
            window.useThisGithubPath = 'muaz-khan/RTCMultiConnection';
        </script>
        <script src="https://cdn.webrtc-experiment.com/commits.js" async></script>

    </article>

    <footer>
        <p>
            <a href="https://www.webrtc-experiment.com">WebRTC Experiments</a> © <a href="https://plus.google.com/+MuazKhan" rel="author" target="_blank">Muaz Khan</a>
            <a href="mailto:muazkh@gmail.com" target="_blank">muazkh@gmail.com</a>
            <a href="https://github.com/muaz-khan" target="_blank">Github</a>
        </p>
    </footer>

</body>

</html>
